Projeto: Uma Jornada pelos Dados da Olist¶

A Análise¶

A compreensão do cenário do comércio eletrônico brasileiro é essencial para identificar tendências e oportunidades no mercado. Este projeto se concentra na análise do conjunto de dados da Olist, uma plataforma de comércio eletrônico que desempenha um papel crucial ao conectar pequenas empresas a canais de vendas online, facilitando a comercialização de produtos em diversos marketplaces no Brasil. Além disso, a Olist oferece serviços de logística e gerenciamento de pedidos para seus clientes.

Objeto de Estudo¶

O foco da análise recai sobre um conjunto de dados abrangente, composto por informações de 100 mil pedidos realizados na loja Olist no período de 2016 a 2018. Esses pedidos foram efetuados em vários marketplaces brasileiros, proporcionando uma visão detalhada das transações comerciais. A análise contempla diversas dimensões, incluindo: Características das Vendas e Receitas, Horários das Vendas, Avaliações dos Clientes, Logística e Entrega.

Essa abordagem multifacetada visa não apenas avaliar o desempenho da plataforma, mas também fornecer insights profundos sobre o funcionamento da Olist. A análise dos dados reais e anonimizados contribui para a compreensão das dinâmicas do mercado, facilitando a identificação de oportunidades de aprimoramento e tomada de decisões estratégicas no setor.

Tópicos¶

  • Impotando Bibliotecas
  • Importando os dados
  • "Cheirando" os Dados
  • Renomeando Colunas
  • Juntando os DataFrames
  • Identificando valores nulos
  • Estatística Descritiva
  • Distribuídas as avaliações (notas)
  • Análise de Pareto
  • Clientes que mais gastam
  • Principais cidades por número de pedidos por estado
  • Cidades com maior geração de receita
  • Pareto das Cidades
  • Variação ao longo do tempo
  • Avaliação dos Produtos
  • O método de pagamento afeta o status do pedido?
  • Há alguma relação entre o tempo de entrega e as pontuações das avaliações?
  • Quais são as cidades dos vendedores com menor/maior tempo de entrega
  • Estados com maior/menor tempo de entrega
  • Como o tempo médio de entrega varia ao longo do tempo?
  • Como a pontuação média das avaliações varia ao longo do tempo
  • Categorias de produtos mais vendidas
  • Mapa coroplético com o tempo de entrega em dias por cada unidade federativa
  • Considerações sobre a Análise
  • Referências

Importando Bibliotecas¶

In [1]:
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt 
import plotly.graph_objects as go
import plotly.express as px
import datetime
import warnings
warnings.filterwarnings("ignore")

Importando os dados¶

Vamos utilizar a base de dados da Olist que está disponível no Kaggle. Para baixar os dados basta se inscrever e baixar aqui

In [2]:
customers_df = pd.read_csv("./data/raw/olist_customers_dataset.csv")
geo_df = pd.read_csv("./data/raw/olist_geolocation_dataset.csv")
orderitem_df = pd.read_csv("./data/raw/olist_order_items_dataset.csv")
orderpay_df = pd.read_csv("./data/raw/olist_order_payments_dataset.csv")
orderreviews_df = pd.read_csv("./data/raw/olist_order_reviews_dataset.csv")
orders_df = pd.read_csv("./data/raw/olist_orders_dataset.csv")
products_df = pd.read_csv("./data/raw/olist_products_dataset.csv")
sellers_df = pd.read_csv("./data/raw/olist_sellers_dataset.csv")
categname_df = pd.read_csv("./data/raw/product_category_name_translation.csv")
pd.set_option('display.max_columns', 500)
In [3]:
# Vendo as 10 linhas do arquivo de clientes
customers_df.head(10)
Out[3]:
customer_id customer_unique_id customer_zip_code_prefix customer_city customer_state
0 06b8999e2fba1a1fbc88172c00ba8bc7 861eff4711a542e4b93843c6dd7febb0 14409 franca SP
1 18955e83d337fd6b2def6b18a428ac77 290c77bc529b7ac935b93aa66c333dc3 9790 sao bernardo do campo SP
2 4e7b3e00288586ebd08712fdd0374a03 060e732b5b29e8181a18229c7b0b2b5e 1151 sao paulo SP
3 b2b6027bc5c5109e529d4dc6358b12c3 259dac757896d24d7702b9acbbff3f3c 8775 mogi das cruzes SP
4 4f2d8ab171c80ec8364f7c12e35b23ad 345ecd01c38d18a9036ed96c73b8d066 13056 campinas SP
5 879864dab9bc3047522c92c82e1212b8 4c93744516667ad3b8f1fb645a3116a4 89254 jaragua do sul SC
6 fd826e7cf63160e536e0908c76c3f441 addec96d2e059c80c30fe6871d30d177 4534 sao paulo SP
7 5e274e7a0c3809e14aba7ad5aae0d407 57b2a98a409812fe9618067b6b8ebe4f 35182 timoteo MG
8 5adf08e34b2e993982a47070956c5c65 1175e95fb47ddff9de6b2b06188f7e0d 81560 curitiba PR
9 4b7139f34592b3a31687243a302fa75b 9afe194fb833f79e300e37e580171f22 30575 belo horizonte MG
In [4]:
# Quantas linhas e colunas tem o dataframe de clientes
customers_df.shape
Out[4]:
(99441, 5)

"Cheirando" os Dados¶

Além de conhecer os metadados, temos também que dar uma "cheiradinha" neles! Visualizar algumas linhas, avaliar os tipos de colunas e como as diferentes bases se relacionam.

In [5]:
print("customers_df:")
display(customers_df.head())

print("\ngeo_df:")
display(geo_df.head())

print("\norderitem_df:")
display(orderitem_df.head())

print("\norderpay_df:")
display(orderpay_df.head())

print("\norderreviews_df:")
display(orderreviews_df.head())

print("\norders_df:")
display(orders_df.head())

print("\nproducts_df:")
display(products_df.head())

print("\nsellers_df:")
display(sellers_df.head())

print("\ncategname_df:")
display(categname_df.head())
customers_df:
customer_id customer_unique_id customer_zip_code_prefix customer_city customer_state
0 06b8999e2fba1a1fbc88172c00ba8bc7 861eff4711a542e4b93843c6dd7febb0 14409 franca SP
1 18955e83d337fd6b2def6b18a428ac77 290c77bc529b7ac935b93aa66c333dc3 9790 sao bernardo do campo SP
2 4e7b3e00288586ebd08712fdd0374a03 060e732b5b29e8181a18229c7b0b2b5e 1151 sao paulo SP
3 b2b6027bc5c5109e529d4dc6358b12c3 259dac757896d24d7702b9acbbff3f3c 8775 mogi das cruzes SP
4 4f2d8ab171c80ec8364f7c12e35b23ad 345ecd01c38d18a9036ed96c73b8d066 13056 campinas SP
geo_df:
geolocation_zip_code_prefix geolocation_lat geolocation_lng geolocation_city geolocation_state
0 1037 -23.545621 -46.639292 sao paulo SP
1 1046 -23.546081 -46.644820 sao paulo SP
2 1046 -23.546129 -46.642951 sao paulo SP
3 1041 -23.544392 -46.639499 sao paulo SP
4 1035 -23.541578 -46.641607 sao paulo SP
orderitem_df:
order_id order_item_id product_id seller_id shipping_limit_date price freight_value
0 00010242fe8c5a6d1ba2dd792cb16214 1 4244733e06e7ecb4970a6e2683c13e61 48436dade18ac8b2bce089ec2a041202 2017-09-19 09:45:35 58.90 13.29
1 00018f77f2f0320c557190d7a144bdd3 1 e5f2d52b802189ee658865ca93d83a8f dd7ddc04e1b6c2c614352b383efe2d36 2017-05-03 11:05:13 239.90 19.93
2 000229ec398224ef6ca0657da4fc703e 1 c777355d18b72b67abbeef9df44fd0fd 5b51032eddd242adc84c38acab88f23d 2018-01-18 14:48:30 199.00 17.87
3 00024acbcdf0a6daa1e931b038114c75 1 7634da152a4610f1595efa32f14722fc 9d7a1d34a5052409006425275ba1c2b4 2018-08-15 10:10:18 12.99 12.79
4 00042b26cf59d7ce69dfabb4e55b4fd9 1 ac6c3623068f30de03045865e4e10089 df560393f3a51e74553ab94004ba5c87 2017-02-13 13:57:51 199.90 18.14
orderpay_df:
order_id payment_sequential payment_type payment_installments payment_value
0 b81ef226f3fe1789b1e8b2acac839d17 1 credit_card 8 99.33
1 a9810da82917af2d9aefd1278f1dcfa0 1 credit_card 1 24.39
2 25e8ea4e93396b6fa0d3dd708e76c1bd 1 credit_card 1 65.71
3 ba78997921bbcdc1373bb41e913ab953 1 credit_card 8 107.78
4 42fdf880ba16b47b59251dd489d4441a 1 credit_card 2 128.45
orderreviews_df:
review_id order_id review_score review_comment_title review_comment_message review_creation_date review_answer_timestamp
0 7bc2406110b926393aa56f80a40eba40 73fc7af87114b39712e6da79b0a377eb 4 NaN NaN 2018-01-18 00:00:00 2018-01-18 21:46:59
1 80e641a11e56f04c1ad469d5645fdfde a548910a1c6147796b98fdf73dbeba33 5 NaN NaN 2018-03-10 00:00:00 2018-03-11 03:05:13
2 228ce5500dc1d8e020d8d1322874b6f0 f9e4b658b201a9f2ecdecbb34bed034b 5 NaN NaN 2018-02-17 00:00:00 2018-02-18 14:36:24
3 e64fb393e7b32834bb789ff8bb30750e 658677c97b385a9be170737859d3511b 5 NaN Recebi bem antes do prazo estipulado. 2017-04-21 00:00:00 2017-04-21 22:02:06
4 f7c4243c7fe1938f181bec41a392bdeb 8e6bfb81e283fa7e4f11123a3fb894f1 5 NaN Parabéns lojas lannister adorei comprar pela I... 2018-03-01 00:00:00 2018-03-02 10:26:53
orders_df:
order_id customer_id order_status order_purchase_timestamp order_approved_at order_delivered_carrier_date order_delivered_customer_date order_estimated_delivery_date
0 e481f51cbdc54678b7cc49136f2d6af7 9ef432eb6251297304e76186b10a928d delivered 2017-10-02 10:56:33 2017-10-02 11:07:15 2017-10-04 19:55:00 2017-10-10 21:25:13 2017-10-18 00:00:00
1 53cdb2fc8bc7dce0b6741e2150273451 b0830fb4747a6c6d20dea0b8c802d7ef delivered 2018-07-24 20:41:37 2018-07-26 03:24:27 2018-07-26 14:31:00 2018-08-07 15:27:45 2018-08-13 00:00:00
2 47770eb9100c2d0c44946d9cf07ec65d 41ce2a54c0b03bf3443c3d931a367089 delivered 2018-08-08 08:38:49 2018-08-08 08:55:23 2018-08-08 13:50:00 2018-08-17 18:06:29 2018-09-04 00:00:00
3 949d5b44dbf5de918fe9c16f97b45f8a f88197465ea7920adcdbec7375364d82 delivered 2017-11-18 19:28:06 2017-11-18 19:45:59 2017-11-22 13:39:59 2017-12-02 00:28:42 2017-12-15 00:00:00
4 ad21c59c0840e6cb83a9ceb5573f8159 8ab97904e6daea8866dbdbc4fb7aad2c delivered 2018-02-13 21:18:39 2018-02-13 22:20:29 2018-02-14 19:46:34 2018-02-16 18:17:02 2018-02-26 00:00:00
products_df:
product_id product_category_name product_name_lenght product_description_lenght product_photos_qty product_weight_g product_length_cm product_height_cm product_width_cm
0 1e9e8ef04dbcff4541ed26657ea517e5 perfumaria 40.0 287.0 1.0 225.0 16.0 10.0 14.0
1 3aa071139cb16b67ca9e5dea641aaa2f artes 44.0 276.0 1.0 1000.0 30.0 18.0 20.0
2 96bd76ec8810374ed1b65e291975717f esporte_lazer 46.0 250.0 1.0 154.0 18.0 9.0 15.0
3 cef67bcfe19066a932b7673e239eb23d bebes 27.0 261.0 1.0 371.0 26.0 4.0 26.0
4 9dc1a7de274444849c219cff195d0b71 utilidades_domesticas 37.0 402.0 4.0 625.0 20.0 17.0 13.0
sellers_df:
seller_id seller_zip_code_prefix seller_city seller_state
0 3442f8959a84dea7ee197c632cb2df15 13023 campinas SP
1 d1b65fc7debc3361ea86b5f14c68d2e2 13844 mogi guacu SP
2 ce3ad9de960102d0677a81f5d0bb7b2d 20031 rio de janeiro RJ
3 c0f3eea2e14555b6faeea3dd58c1b1c3 4195 sao paulo SP
4 51a04a8a6bdcb23deccc82b0b80742cf 12914 braganca paulista SP
categname_df:
product_category_name product_category_name_english
0 beleza_saude health_beauty
1 informatica_acessorios computers_accessories
2 automotivo auto
3 cama_mesa_banho bed_bath_table
4 moveis_decoracao furniture_decor
In [6]:
print("customers_df:\n")
display(customers_df.info())

print("\ngeo_df:\n")
display(geo_df.info())

print("\norderitem_df:\n")
display(orderitem_df.info())

print("\norderpay_df:\n")
display(orderpay_df.info())

print("\norderreviews_df:\n")
display(orderreviews_df.info())

print("\norders_df:\n")
display(orders_df.info())

print("\nproducts_df:\n")
display(products_df.info())

print("\nsellers_df:\n")
display(sellers_df.info())

print("\ncategname_df:\n")
display(categname_df.info())
customers_df:

<class 'pandas.core.frame.DataFrame'>
RangeIndex: 99441 entries, 0 to 99440
Data columns (total 5 columns):
 #   Column                    Non-Null Count  Dtype 
---  ------                    --------------  ----- 
 0   customer_id               99441 non-null  object
 1   customer_unique_id        99441 non-null  object
 2   customer_zip_code_prefix  99441 non-null  int64 
 3   customer_city             99441 non-null  object
 4   customer_state            99441 non-null  object
dtypes: int64(1), object(4)
memory usage: 3.8+ MB
None
geo_df:

<class 'pandas.core.frame.DataFrame'>
RangeIndex: 1000163 entries, 0 to 1000162
Data columns (total 5 columns):
 #   Column                       Non-Null Count    Dtype  
---  ------                       --------------    -----  
 0   geolocation_zip_code_prefix  1000163 non-null  int64  
 1   geolocation_lat              1000163 non-null  float64
 2   geolocation_lng              1000163 non-null  float64
 3   geolocation_city             1000163 non-null  object 
 4   geolocation_state            1000163 non-null  object 
dtypes: float64(2), int64(1), object(2)
memory usage: 38.2+ MB
None
orderitem_df:

<class 'pandas.core.frame.DataFrame'>
RangeIndex: 112650 entries, 0 to 112649
Data columns (total 7 columns):
 #   Column               Non-Null Count   Dtype  
---  ------               --------------   -----  
 0   order_id             112650 non-null  object 
 1   order_item_id        112650 non-null  int64  
 2   product_id           112650 non-null  object 
 3   seller_id            112650 non-null  object 
 4   shipping_limit_date  112650 non-null  object 
 5   price                112650 non-null  float64
 6   freight_value        112650 non-null  float64
dtypes: float64(2), int64(1), object(4)
memory usage: 6.0+ MB
None
orderpay_df:

<class 'pandas.core.frame.DataFrame'>
RangeIndex: 103886 entries, 0 to 103885
Data columns (total 5 columns):
 #   Column                Non-Null Count   Dtype  
---  ------                --------------   -----  
 0   order_id              103886 non-null  object 
 1   payment_sequential    103886 non-null  int64  
 2   payment_type          103886 non-null  object 
 3   payment_installments  103886 non-null  int64  
 4   payment_value         103886 non-null  float64
dtypes: float64(1), int64(2), object(2)
memory usage: 4.0+ MB
None
orderreviews_df:

<class 'pandas.core.frame.DataFrame'>
RangeIndex: 99224 entries, 0 to 99223
Data columns (total 7 columns):
 #   Column                   Non-Null Count  Dtype 
---  ------                   --------------  ----- 
 0   review_id                99224 non-null  object
 1   order_id                 99224 non-null  object
 2   review_score             99224 non-null  int64 
 3   review_comment_title     11568 non-null  object
 4   review_comment_message   40977 non-null  object
 5   review_creation_date     99224 non-null  object
 6   review_answer_timestamp  99224 non-null  object
dtypes: int64(1), object(6)
memory usage: 5.3+ MB
None
orders_df:

<class 'pandas.core.frame.DataFrame'>
RangeIndex: 99441 entries, 0 to 99440
Data columns (total 8 columns):
 #   Column                         Non-Null Count  Dtype 
---  ------                         --------------  ----- 
 0   order_id                       99441 non-null  object
 1   customer_id                    99441 non-null  object
 2   order_status                   99441 non-null  object
 3   order_purchase_timestamp       99441 non-null  object
 4   order_approved_at              99281 non-null  object
 5   order_delivered_carrier_date   97658 non-null  object
 6   order_delivered_customer_date  96476 non-null  object
 7   order_estimated_delivery_date  99441 non-null  object
dtypes: object(8)
memory usage: 6.1+ MB
None
products_df:

<class 'pandas.core.frame.DataFrame'>
RangeIndex: 32951 entries, 0 to 32950
Data columns (total 9 columns):
 #   Column                      Non-Null Count  Dtype  
---  ------                      --------------  -----  
 0   product_id                  32951 non-null  object 
 1   product_category_name       32341 non-null  object 
 2   product_name_lenght         32341 non-null  float64
 3   product_description_lenght  32341 non-null  float64
 4   product_photos_qty          32341 non-null  float64
 5   product_weight_g            32949 non-null  float64
 6   product_length_cm           32949 non-null  float64
 7   product_height_cm           32949 non-null  float64
 8   product_width_cm            32949 non-null  float64
dtypes: float64(7), object(2)
memory usage: 2.3+ MB
None
sellers_df:

<class 'pandas.core.frame.DataFrame'>
RangeIndex: 3095 entries, 0 to 3094
Data columns (total 4 columns):
 #   Column                  Non-Null Count  Dtype 
---  ------                  --------------  ----- 
 0   seller_id               3095 non-null   object
 1   seller_zip_code_prefix  3095 non-null   int64 
 2   seller_city             3095 non-null   object
 3   seller_state            3095 non-null   object
dtypes: int64(1), object(3)
memory usage: 96.8+ KB
None
categname_df:

<class 'pandas.core.frame.DataFrame'>
RangeIndex: 71 entries, 0 to 70
Data columns (total 2 columns):
 #   Column                         Non-Null Count  Dtype 
---  ------                         --------------  ----- 
 0   product_category_name          71 non-null     object
 1   product_category_name_english  71 non-null     object
dtypes: object(2)
memory usage: 1.2+ KB
None

Renomeando Colunas¶

Neste trecho de código iremos renomear a coluna customer_zip_code_prefix do DataFrame customers_df para zip_code e a coluna geolocation_zip_code_prefix do DataFrame geo_df para zip_code, usando o método rename() da biblioteca pandas.

Renomear colunas pode ser útil em várias situações, como quando as colunas têm nomes longos e complicados, nomes com caracteres especiais ou para tornar os nomes das colunas mais descritivos e padronizados. Nesse caso, é somente para facilitar juntarmos os dados das bases de clientes e de geolocalizacao (por meio do CEP)

In [7]:
customers_df = customers_df.rename(columns={"customer_zip_code_prefix": "zip_code"})
geo_df = geo_df.rename(columns={"geolocation_zip_code_prefix": "zip_code"})
In [8]:
customers_df.head()
Out[8]:
customer_id customer_unique_id zip_code customer_city customer_state
0 06b8999e2fba1a1fbc88172c00ba8bc7 861eff4711a542e4b93843c6dd7febb0 14409 franca SP
1 18955e83d337fd6b2def6b18a428ac77 290c77bc529b7ac935b93aa66c333dc3 9790 sao bernardo do campo SP
2 4e7b3e00288586ebd08712fdd0374a03 060e732b5b29e8181a18229c7b0b2b5e 1151 sao paulo SP
3 b2b6027bc5c5109e529d4dc6358b12c3 259dac757896d24d7702b9acbbff3f3c 8775 mogi das cruzes SP
4 4f2d8ab171c80ec8364f7c12e35b23ad 345ecd01c38d18a9036ed96c73b8d066 13056 campinas SP

Juntando os DataFrames¶

Este trecho de código combina os DataFrames orders_df, customers_df, orderitem_df, products_df, categname_df, orderpay_df, sellers_df e orderreviews_df, usando o método merge() da biblioteca pandas. Cada DataFrame é combinado com o próximo usando uma coluna em comum como chave de junção.

Esse processo de combinar vários DataFrames em um só é útil para consolidar dados dispersos em um único lugar e facilitar a análise dos dados.

In [9]:
data = orders_df.merge(customers_df, on="customer_id").merge(orderitem_df, on="order_id").merge(products_df, on="product_id").merge(categname_df, on="product_category_name").merge(orderpay_df, on="order_id").merge(sellers_df, on="seller_id").merge(orderreviews_df, on="order_id")
In [10]:
data.head()
Out[10]:
order_id customer_id order_status order_purchase_timestamp order_approved_at order_delivered_carrier_date order_delivered_customer_date order_estimated_delivery_date customer_unique_id zip_code customer_city customer_state order_item_id product_id seller_id shipping_limit_date price freight_value product_category_name product_name_lenght product_description_lenght product_photos_qty product_weight_g product_length_cm product_height_cm product_width_cm product_category_name_english payment_sequential payment_type payment_installments payment_value seller_zip_code_prefix seller_city seller_state review_id review_score review_comment_title review_comment_message review_creation_date review_answer_timestamp
0 e481f51cbdc54678b7cc49136f2d6af7 9ef432eb6251297304e76186b10a928d delivered 2017-10-02 10:56:33 2017-10-02 11:07:15 2017-10-04 19:55:00 2017-10-10 21:25:13 2017-10-18 00:00:00 7c396fd4830fd04220f754e42b4e5bff 3149 sao paulo SP 1 87285b34884572647811a353c7ac498a 3504c0cb71d7fa48d967e0e4c94d59d9 2017-10-06 11:07:15 29.99 8.72 utilidades_domesticas 40.0 268.0 4.0 500.0 19.0 8.0 13.0 housewares 1 credit_card 1 18.12 9350 maua SP a54f0611adc9ed256b57ede6b6eb5114 4 NaN Não testei o produto ainda, mas ele veio corre... 2017-10-11 00:00:00 2017-10-12 03:43:48
1 e481f51cbdc54678b7cc49136f2d6af7 9ef432eb6251297304e76186b10a928d delivered 2017-10-02 10:56:33 2017-10-02 11:07:15 2017-10-04 19:55:00 2017-10-10 21:25:13 2017-10-18 00:00:00 7c396fd4830fd04220f754e42b4e5bff 3149 sao paulo SP 1 87285b34884572647811a353c7ac498a 3504c0cb71d7fa48d967e0e4c94d59d9 2017-10-06 11:07:15 29.99 8.72 utilidades_domesticas 40.0 268.0 4.0 500.0 19.0 8.0 13.0 housewares 3 voucher 1 2.00 9350 maua SP a54f0611adc9ed256b57ede6b6eb5114 4 NaN Não testei o produto ainda, mas ele veio corre... 2017-10-11 00:00:00 2017-10-12 03:43:48
2 e481f51cbdc54678b7cc49136f2d6af7 9ef432eb6251297304e76186b10a928d delivered 2017-10-02 10:56:33 2017-10-02 11:07:15 2017-10-04 19:55:00 2017-10-10 21:25:13 2017-10-18 00:00:00 7c396fd4830fd04220f754e42b4e5bff 3149 sao paulo SP 1 87285b34884572647811a353c7ac498a 3504c0cb71d7fa48d967e0e4c94d59d9 2017-10-06 11:07:15 29.99 8.72 utilidades_domesticas 40.0 268.0 4.0 500.0 19.0 8.0 13.0 housewares 2 voucher 1 18.59 9350 maua SP a54f0611adc9ed256b57ede6b6eb5114 4 NaN Não testei o produto ainda, mas ele veio corre... 2017-10-11 00:00:00 2017-10-12 03:43:48
3 128e10d95713541c87cd1a2e48201934 a20e8105f23924cd00833fd87daa0831 delivered 2017-08-15 18:29:31 2017-08-15 20:05:16 2017-08-17 15:28:33 2017-08-18 14:44:43 2017-08-28 00:00:00 3a51803cc0d012c3b5dc8b7528cb05f7 3366 sao paulo SP 1 87285b34884572647811a353c7ac498a 3504c0cb71d7fa48d967e0e4c94d59d9 2017-08-21 20:05:16 29.99 7.78 utilidades_domesticas 40.0 268.0 4.0 500.0 19.0 8.0 13.0 housewares 1 credit_card 3 37.77 9350 maua SP b46f1e34512b0f4c74a72398b03ca788 4 NaN Deveriam embalar melhor o produto. A caixa vei... 2017-08-19 00:00:00 2017-08-20 15:16:36
4 0e7e841ddf8f8f2de2bad69267ecfbcf 26c7ac168e1433912a51b924fbd34d34 delivered 2017-08-02 18:24:47 2017-08-02 18:43:15 2017-08-04 17:35:43 2017-08-07 18:30:01 2017-08-15 00:00:00 ef0996a1a279c26e7ecbd737be23d235 2290 sao paulo SP 1 87285b34884572647811a353c7ac498a 3504c0cb71d7fa48d967e0e4c94d59d9 2017-08-08 18:37:31 29.99 7.78 utilidades_domesticas 40.0 268.0 4.0 500.0 19.0 8.0 13.0 housewares 1 credit_card 1 37.77 9350 maua SP dc90f19c2806f1abba9e72ad3c350073 5 NaN Só achei ela pequena pra seis xícaras ,mais é ... 2017-08-08 00:00:00 2017-08-08 23:26:23

Temos ids de pedidos duplicados. Isso se deve ao fato de que o mesmo pedido pode ser pago com vários métodos de pagamento diferentes.

Identificando valores nulos¶

In [11]:
# Porcentagem de valores nulos
(100 * data.isna().sum() / len(data) ).sort_values(ascending=False)
Out[11]:
review_comment_title             88.062348
review_comment_message           57.697065
order_delivered_customer_date     2.075963
order_delivered_carrier_date      1.033657
order_approved_at                 0.012110
product_length_cm                 0.000865
product_height_cm                 0.000865
product_width_cm                  0.000865
product_weight_g                  0.000865
payment_installments              0.000000
product_category_name_english     0.000000
payment_sequential                0.000000
payment_type                      0.000000
order_id                          0.000000
payment_value                     0.000000
seller_zip_code_prefix            0.000000
seller_city                       0.000000
review_id                         0.000000
review_score                      0.000000
review_creation_date              0.000000
seller_state                      0.000000
product_description_lenght        0.000000
product_photos_qty                0.000000
customer_id                       0.000000
order_status                      0.000000
order_purchase_timestamp          0.000000
order_estimated_delivery_date     0.000000
customer_unique_id                0.000000
zip_code                          0.000000
customer_city                     0.000000
customer_state                    0.000000
order_item_id                     0.000000
product_id                        0.000000
seller_id                         0.000000
shipping_limit_date               0.000000
price                             0.000000
freight_value                     0.000000
product_category_name             0.000000
product_name_lenght               0.000000
review_answer_timestamp           0.000000
dtype: float64

Estatística Descritiva¶

In [12]:
data.describe()
Out[12]:
zip_code order_item_id price freight_value product_name_lenght product_description_lenght product_photos_qty product_weight_g product_length_cm product_height_cm product_width_cm payment_sequential payment_installments payment_value seller_zip_code_prefix review_score
count 115609.000000 115609.000000 115609.000000 115609.000000 115609.000000 115609.000000 115609.000000 115608.000000 115608.000000 115608.000000 115608.000000 115609.000000 115609.000000 115609.000000 115609.000000 115609.000000
mean 35061.537597 1.194535 120.619850 20.056880 48.766541 785.808198 2.205373 2113.907697 30.307903 16.638477 23.113167 1.093747 2.946233 172.387379 24515.713958 4.034409
std 29841.671732 0.685926 182.653476 15.836184 10.034187 652.418619 1.717771 3781.754895 16.211108 13.473570 11.755083 0.729849 2.781087 265.873969 27636.640968 1.385584
min 1003.000000 1.000000 0.850000 0.000000 5.000000 4.000000 1.000000 0.000000 7.000000 2.000000 6.000000 1.000000 0.000000 0.000000 1001.000000 1.000000
25% 11310.000000 1.000000 39.900000 13.080000 42.000000 346.000000 1.000000 300.000000 18.000000 8.000000 15.000000 1.000000 1.000000 60.870000 6429.000000 4.000000
50% 24241.000000 1.000000 74.900000 16.320000 52.000000 600.000000 1.000000 700.000000 25.000000 13.000000 20.000000 1.000000 2.000000 108.050000 13660.000000 5.000000
75% 58745.000000 1.000000 134.900000 21.210000 57.000000 983.000000 3.000000 1800.000000 38.000000 20.000000 30.000000 1.000000 4.000000 189.480000 28605.000000 5.000000
max 99980.000000 21.000000 6735.000000 409.680000 76.000000 3992.000000 20.000000 40425.000000 105.000000 105.000000 118.000000 29.000000 24.000000 13664.080000 99730.000000 5.000000

Distribuídas as avaliações (notas)¶

In [13]:
# Criando a percentagem
percentage = data["review_score"].value_counts(normalize=True) * 100

#Gráfico
fig = px.bar(percentage, x=percentage.index, y=percentage.values, text=percentage.values, labels={"x": "Nota", "y": "Percentagem"})
fig.update_traces(texttemplate='%{text:.2f}%', textposition='outside')
fig.show()

Mais de 75% dos clientes deram uma pontuação igual ou maior que 4. 12,5% deram uma pontuação de 1 e cerca de 12% deram uma pontuação de 3 ou 2.

Análise de Pareto¶

Análise de Pareto é uma técnica importante para identificar e priorizar os elementos mais relevantes em um conjunto de dados, permitindo a concentração de esforços nas áreas mais críticas e relevantes, maximizando resultados e minimizando custos. É uma ferramenta simples e de fácil aplicação.

É a famosa regra 20/80, 20% que representam 80% do resultado

Podemos utilizar para várias análises! Vamos começar com a proporção de clientes que mais gastam.

In [14]:
# Agrupando os clientes com o maior número cumulativo de pedidos (em pagamentos).
top_customers = data.groupby("customer_unique_id")["payment_value"].sum().reset_index().sort_values("payment_value", ascending=False)
top_customers.rename(columns={"payment_value":"total_paid"}, inplace=True)
In [15]:
# calculaando as colunas "% of Total Sales" e "Cum % of Total Sales"
top_customers["% of Total Sales"] = (top_customers["total_paid"] / top_customers["total_paid"].sum()) * 100
top_customers["Cum % of Total Sales"] = top_customers["% of Total Sales"].cumsum()
In [16]:
# criaando um gráfico de linhas do Plotly
fig = px.line(top_customers, x=range(1, len(top_customers) + 1), y="Cum % of Total Sales")

# definir as etiquetas do eixo x e y e o título do gráfico
fig.update_layout(
    xaxis_title="Número de Clientes",
    yaxis_title="Total Vendas Cumulativo %",
    title="Contribuição % para as vendas por número de clientes"
)

# adicionando uma linha de preenchimento abaixo do gráfico
fig.add_shape(
    type="rect",
    xref="x",
    yref="paper",
    x0=0,
    y0=0,
    x1=40000,
    y1=1,
    fillcolor="green",
    opacity=0.2,
    layer="below"
)

# atualizando o layout da forma para ajustar a altura do preenchimento
fig.update_shapes(dict(xref='x', yref='paper'))

# adicionando um texto explicativo na figura
fig.add_annotation(
    x=55000,
    y=75,
    text="40k clientes (+-42% do total)<br> representam +-80% das vendas",
    font=dict(
        size=14,
        color="black"
    ),
    showarrow=False,
)

fig.show()

A partir desta Análise de Pareto, destacamos dois pontos-chave:

Concentração de Receita: Identificamos que cerca de 40% dos clientes correspondem a 80% da receita total. Isso sublinha a importância estratégica de cultivar relacionamentos sólidos com esse grupo seleto de clientes-chave.

Otimização de Recursos: Recursos preciosos, como tempo, esforço e orçamento de marketing, podem ser otimizados ao se concentrar nos clientes de alto valor. Recomendo considerar estratégias específicas, como marketing direcionado, atendimento personalizado e programas de recompensa para maximizar o valor desses clientes.

Riscos e Diversificação: É crucial ponderar os riscos associados à dependência de um pequeno grupo de clientes. Diversificar as fontes de receita pode ser uma estratégia prudente, mitigando potenciais impactos negativos e proporcionando uma base mais estável para o crescimento futuro.

Clientes que mais gastam¶

Os resultados dessa análise podem gerar diversos insights para a área de marketing! Por exemplo:

  • Monitorar os clientes que mais gastam-
  • Oferecer promoções
  • Tentar vender mais produtos
  • Realizar entregas personalizadas
In [17]:
# renomeaNDO a coluna "payment_value" para "total_paid"
top_customers.rename(columns={"payment_value" : "total_paid"}, inplace=True)

# criaNDO um gráfico de barras do Plotly
fig = px.bar(top_customers[:10], x="total_paid", y="customer_unique_id", orientation="h")

# atualizando as configurações de layout do gráfico
fig.update_layout(
    title="Top 10 Clientes por Valor Total Pago",
    xaxis_title="Valor Total Pago",
    yaxis_title="ID do Cliente Único"
)

fig.show()

Principais cidades por número de pedidos por estado¶

Vamos verificar quais são as cidades que mais tem números de pedido!

In [18]:
# agrupando o dataframe por "customer_state" e contar o número de "order_id" por estado
top_orders_cities = data.groupby("customer_state")["order_id"].count().reset_index().sort_values("order_id", ascending=False)

# renomeando a coluna "order_id" para "count"
top_orders_cities.rename(columns={"order_id":"count"}, inplace=True)

# criando um gráfico de barras do Plotly
fig = px.bar(top_orders_cities[:10], x="count", y="customer_state", orientation="h")

# atualizando as configurações de layout do gráfico
fig.update_layout(
    title="TOP 10 Estados por Número de Pedidos",
    xaxis_title="Número de Pedidos",
    yaxis_title="Estado"
)

fig.show()

Cidades com maior geração de receita¶

Agora podemos avaliar não a quantidade de pedidos mas o quanto de dinheiro que cada cidade traz

In [19]:
# Fazendo agrupamento e contagem
top_ordersbyvalue_cities = data.groupby("customer_city")["payment_value"].sum().reset_index().sort_values("payment_value", ascending=False)
top_ordersbyvalue_cities["% of Total Payments"] = (top_ordersbyvalue_cities["payment_value"] / top_ordersbyvalue_cities["payment_value"].sum()) * 100
top_ordersbyvalue_cities["Cum % of Total Payments"] = top_ordersbyvalue_cities["% of Total Payments"].cumsum() 
In [20]:
# criando um gráfico de barras do Plotly
fig = px.bar(top_ordersbyvalue_cities[:10], x="% of Total Payments", y="customer_city", orientation="h")

# atualizando as configurações de layout do gráfico
fig.update_layout(
    title="TOP 10 Cidades por Geração de Receita",
    xaxis_title="Porcentagem do Total de Pagamentos",
    yaxis_title="Cidade do Cliente"
)

fig.show()

Liderança de São Paulo: O Estado de São Paulo lidera em número de pedidos, indicando uma alta atividade comercial. A capital, São Paulo, contribui com cerca de 14% da receita total, ressaltando a importância econômica dessa região.

Concentração de Receita em São Paulo: A concentração de receita em São Paulo, Campinas e Guarulhos sugere que essas cidades possuem consumidores que realizam compras de maior valor. Isso pode ser explorado através de estratégias específicas de marketing e atendimento ao cliente nessas áreas.

Desempenho Significativo de Brasília: Apesar do Distrito Federal não estar entre os estados com o maior número de vendas, a cidade de Brasília ocupa a quarta posição entre as cidades com maiores receitas. Isso aponta para um comportamento de compra significativo na região, o que pode indicar um mercado de alto valor em Brasília.

Análise de Pareto para Cidades: A análise de Pareto revela que apenas cerca de 9% das cidades representam 80% das vendas. Isso sugere uma concentração notável de atividade econômica em um número limitado de locais.

Foco Estratégico em Cidades de Alto Desempenho: Considerando a análise de Pareto, estratégias de marketing, expansão de serviços e parcerias podem ser otimizadas concentrando-se nas cidades que contribuem significativamente para as vendas. Isso pode resultar em uma alocação mais eficiente de recursos e um aumento geral na receita.

Segmentação e Personalização: A concentração de vendas em algumas cidades sugere a importância de segmentar e personalizar abordagens de marketing e serviço ao cliente para atender às demandas específicas dessas áreas-chave.

Pareto das Cidades¶

In [21]:
# criando um gráfico de linha do Plotly
fig = px.line(top_ordersbyvalue_cities, x=range(1, len(top_ordersbyvalue_cities)+1), y="Cum % of Total Payments")

# atualizando as configurações de layout do gráfico
fig.update_layout(
    title="% de Contribuição das Vendas por Número de Cidades",
    xaxis_title="Número de Cidades",
    yaxis_title="% de Contribuição para as Vendas"
)

# preenchendo a área abaixo da curva
fig.add_shape(
    type="rect",
    xref="x",
    yref="y",
    x0=0,
    y0=0,
    x1=358,
    y1=top_ordersbyvalue_cities["Cum % of Total Payments"][357],
    fillcolor="green",
    opacity=0.3,
    layer="below",
    line_width=0
)

# adicionando um texto ao gráfico
fig.add_annotation(
    x=1000,
    y=70,
    text="358 cidades (+-8,7% do total) <br>contribuem para +-80% das vendas.",
    font=dict(
        size=14,
        color="black"
    ),
    showarrow=False
)

fig.show()
In [22]:
print("Número de cidades que contribuem com 80% das vendas totais:",
      len(top_ordersbyvalue_cities[top_ordersbyvalue_cities["Cum % of Total Payments"] <= 80]),
      "ou em in %:",
      (len(top_ordersbyvalue_cities[top_ordersbyvalue_cities["Cum % of Total Payments"] <= 80]) / len(top_ordersbyvalue_cities)) * 100)
Número de cidades que contribuem com 80% das vendas totais: 358 ou em in %: 8.746640605912534

Variação ao longo do tempo¶

Como os pedidos são feitos ao longo do dia? De manhã? De tarde? Na madruga? Às segundas, sábados? É importante saber se estamos vendendo mais ou menos, nos horários e dias das semana!

In [23]:
# Convertendo as colunas de datas para datetime
datesCols = ["order_purchase_timestamp", "order_approved_at", "order_delivered_carrier_date", 
            "order_delivered_customer_date", "order_estimated_delivery_date", "shipping_limit_date", 
            "review_creation_date", "review_answer_timestamp"]

for col in datesCols:
    data[col] = pd.to_datetime(data[col])
In [24]:
# Ordenando por hora
orders_df["order_purchase_timestamp"] = pd.to_datetime(orders_df["order_purchase_timestamp"])
orderbyhour = orders_df.groupby(orders_df["order_purchase_timestamp"].dt.hour)["order_id"].count().reset_index().sort_values(by="order_purchase_timestamp", ascending=False)
orderbyhour.rename(columns={"order_id":"Total Orders", "order_purchase_timestamp": "Hour of Day"}, inplace=True)
In [25]:
# Gráfico
fig = px.bar(orderbyhour, x='Hour of Day', y='Total Orders', title='Número de pedidos por hora do dia')
fig.update_xaxes(title='Hora do dia')
fig.update_yaxes(title='Número total de pedidos')
fig.show()

Os pedidos começam a aumentar por volta das 6h da manhã e atingem o pico às 4 da tarde. Os pedidom se mantem em uma boa margem até as 22h antes de começarem a cair.

Picos de Atividade: O pico de pedidos às 16h sugere um momento de alta demanda. Pode ser benéfico otimizar recursos, como equipe e logística, para lidar eficientemente com esse aumento de atividade.

Horários Estratégicos para Promoções: Considerando que os pedidos permanecem em uma boa margem até as 22h, este período pode ser estrategicamente explorado para promoções especiais ou ofertas que incentivem compras durante as últimas horas do dia.

Gestão de Estoque: Entender os horários de pico e declínio pode ajudar na gestão eficaz do estoque, garantindo que haja produtos disponíveis durante os momentos de maior demanda e evitando excessos durante os períodos de menor atividade.

Otimização de Entregas e Logística: Com o conhecimento dos horários de pico, a otimização das operações logísticas pode ser implementada para garantir entregas rápidas e eficientes nos momentos em que a demanda é mais alta.

Personalização de Experiência do Cliente: Adaptar estratégias de marketing e atendimento ao cliente para coincidir com os horários de maior atividade pode melhorar a experiência do cliente durante esses períodos críticos.

Análise de Tendências ao Longo do Dia: Observar como os padrões de pedidos variam ao longo do dia pode fornecer insights sobre o comportamento do consumidor e orientar ações específicas para maximizar as oportunidades em diferentes momentos.

In [26]:
# Ordenandopor dia da semana
orderbydow = data.groupby(data["order_purchase_timestamp"].dt.day_name())["order_id"].count().reset_index()
orderbydow.rename(columns={"order_id":"Total Orders", "order_purchase_timestamp": "Weekday Name"}, inplace=True)
orderbydow = orderbydow.sort_values(by="Total Orders", ascending=False)
In [27]:
# Gráfico
fig = px.bar(orderbydow, x='Weekday Name', y='Total Orders', title='Número de pedidos por dia da semana')
fig.update_xaxes(title='Dia da semana')
fig.update_yaxes(title='Número total de pedidos')
fig.show()
In [28]:
# Definindo a ordem dos dias da semana
weekday_order = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday']

# Convertendo a coluna "Weekday Name" em uma categoria ordenada
weekday = pd.Categorical(orderbydow['Weekday Name'], categories=weekday_order, ordered=True)

# Criando um novo dataframe ordenado pela coluna de categoria "weekday"
orderbydow_ordered = orderbydow.assign(weekday=weekday).sort_values('weekday')

# Criando um gráfico de barras com os dias da semana em ordem
fig = px.bar(orderbydow_ordered, x='weekday', y='Total Orders', title='Número de pedidos por dia da semana')
fig.update_xaxes(title='Dia da semana')
fig.update_yaxes(title='Número total de pedidos')
fig.show()

Os pedidos atingem o pico no início da semana (segunda e terça-feira) e começam a declinar um pouco depois. Durante o final de semana, observa-se uma diminuição acentuada dos pedidos.

Avaliação dos Produtos¶

De que adianta vender muito, entregar rápido, se as notas dos clientes estiverem baixas? Isso queima o filme de qualquer negócio! Vamos entrar mais a fundo na questão.

In [29]:
# Agrupando - Melhores avaliações
reviewsocres = data.groupby("product_category_name")["review_score"].agg(["mean", "count"]).sort_values(by="mean",ascending=False)
bestrated = reviewsocres[reviewsocres["count"]>=30][:10]

# Gráfico
fig = go.Figure(go.Bar(
            x=bestrated['mean'],
            y=bestrated.index,
            orientation='h'))

fig.update_layout(
    title='Produtos com as melhores avaliações',
    xaxis_title='Avaliação média',
    yaxis_title='Categoria do produto',
    height=600,
    width=800,
    margin=dict(l=100, r=20, t=50, b=50),
)

fig.show()
In [30]:
# Agrupando - piores avaliações
reviewsocres = data.groupby("product_category_name")["review_score"].agg(["mean", "count"]).sort_values(by="mean",ascending=False)
worstrated = reviewsocres[reviewsocres["count"]>=30].sort_values(by='mean')[:10]

# Gráfico
fig = go.Figure(go.Bar(
            x=worstrated['mean'],
            y=worstrated.index,
            orientation='h'))

fig.update_layout(
    title='Produtos com as piores avaliações',
    xaxis_title='Avaliação média',
    yaxis_title='Categoria do produto',
    height=600,
    width=800,
    margin=dict(l=100, r=20, t=50, b=50),
)

fig.show()

Entre as categorias nos dois top 10, os livros gerais e importados obtêm as pontuações mais elevadas, registrando 4,4. Em contrapartida, as fraldas recebem as notas mais baixas, marcando 3,2, seguidas pelos móveis de escritório, que alcançam 3,5.

Previsibilidade e Experiência do Cliente: Livros, sendo produtos gerais e importados, provavelmente recebem pontuações mais altas devido à previsibilidade do conteúdo. Os clientes frequentemente têm uma ideia clara do que esperar, contribuindo para uma experiência mais controlada e, portanto, avaliações mais positivas.

Complexidade e Variedade de Produtos: Produtos como fraldas e móveis de escritório são mais complexos, com uma variedade de características que podem influenciar a satisfação do cliente. A qualidade, o tamanho, a durabilidade e outros fatores podem afetar a experiência, resultando em uma gama mais ampla de opiniões e, consequentemente, pontuações mais variadas.

Expectativas do Cliente: A alta pontuação para livros pode indicar que os clientes geralmente têm expectativas claras ao comprar esses produtos. No entanto, para itens como fraldas e móveis de escritório, as expectativas podem ser mais diversas, levando a uma variação nas avaliações.

Avaliação Subjetiva vs. Objetiva: As pontuações mais baixas para fraldas e móveis de escritório podem refletir avaliações mais subjetivas, influenciadas por preferências individuais e experiências pessoais. Isso destaca a importância de considerar a natureza subjetiva das avaliações ao interpretar os resultados.

Gestão de Expectativas: Os resultados sugerem a importância da gestão eficaz das expectativas do cliente para produtos mais complexos. Comunicar claramente as características e garantir uma experiência positiva desde a compra até o uso pode impactar significativamente nas avaliações.

O método de pagamento afeta o status do pedido?¶

In [31]:
# Por exemplo: o pagamento em dinheiro aumenta o cancelamento de pedidos?
cashvscancel = pd.crosstab(data["payment_type"], data["order_status"])
cashvscancel = cashvscancel[["canceled", "delivered"]]
cashvscancel["% Canceled"] = (cashvscancel["canceled"] / cashvscancel["delivered"] ) * 100
cashvscancel["Avg Cancelation Rate"] = (len(data[data["order_status"] == "canceled"]) / len(data[data["order_status"] == "delivered"])) * 100
cashvscancel
Out[31]:
order_status canceled delivered % Canceled Avg Cancelation Rate
payment_type
boleto 92 22029 0.417631 0.473456
credit_card 411 83536 0.492003 0.473456
debit_card 6 1623 0.369686 0.473456
voucher 27 6022 0.448356 0.473456

Podemos ver que a taxa de cancelamento é praticamente a mesma em todos os métodos de pagamento. Mas notamos uma leve desvio acima da média para o cartão de crédito.

Há alguma relação entre o tempo de entrega e as pontuações das avaliações?¶

In [32]:
# Adicionando uma coluna delta que calcula o tempo que levou para o pedido ser entregue
data["TimeToDeliveryinHours"] = (data["order_delivered_customer_date"] - data["order_purchase_timestamp"])
data["TimeToDeliveryinHours"] = data["TimeToDeliveryinHours"].apply(lambda x: x.total_seconds())
data["TimeToDeliveryinHours"] = round((data["TimeToDeliveryinHours"] / 3600) / 24, 2)
data.rename(columns={"TimeToDeliveryinHours" : "TimeToDeliveryinDays"}, inplace=True)
In [33]:
# Principais estatísticas do tempo de entrega
data[["TimeToDeliveryinDays"]].describe()
Out[33]:
TimeToDeliveryinDays
count 113209.000000
mean 12.442130
std 9.356005
min 0.530000
25% 6.740000
50% 10.190000
75% 15.500000
max 208.350000

O tempo médio de entrega é relativamente alto (12,44 dias), com mediana de (10,19 dias). Observamos um valor extremo atípico de 208 dias.

Vamos dar uma olhada pelo boxplot:

In [34]:
# Definindo a ordem crescente dos valores da pontuação de avaliação
score_order = sorted(data['review_score'].unique())

# Convertendo a coluna "review_score" em uma categoria ordenada
score = pd.Categorical(data['review_score'], categories=score_order, ordered=True)

# Criando um novo dataframe ordenado pela coluna de categoria "score"
data_ordered = data.assign(score=score).sort_values('score')

# Criando um seletor de pontuação de avaliação em ordem crescente
fig = px.box(data_ordered, x='score', y='TimeToDeliveryinDays', color='score',
             title='Relação entre a pontuação da avaliação e o tempo de entrega')

fig.update_xaxes(title='Pontuação da avaliação')
fig.update_yaxes(title='Tempo de entrega (dias)')

fig.show()

Podemos ver que temos vários valores atípicos. Estes são pedidos que demoraram muito para serem entregues por algum motivo.

In [35]:
# Definindo o limite superior dos valores
q_high = data["TimeToDeliveryinDays"].quantile(0.95)

# Criando um novo dataframe sem os valores atípicos
data_no_outliers = data[data["TimeToDeliveryinDays"] < q_high]

# Definindo a ordem crescente dos valores da pontuação de avaliação
score_order = sorted(data_no_outliers['review_score'].unique())

# Convertendo a coluna "review_score" em uma categoria ordenada
score = pd.Categorical(data_no_outliers['review_score'], categories=score_order, ordered=True)

# Criando um novo dataframe com a coluna de categoria "score"
data_score = data_no_outliers.assign(score=score)

data_score = data_score.assign(score=score).sort_values('score')

# Criando um seletor de pontuação de avaliação em ordem crescente
fig = px.box(data_score, x='score', y='TimeToDeliveryinDays', color='score',
             title='Relação entre a pontuação da avaliação e o tempo de entrega')

fig.update_xaxes(title='Pontuação da avaliação', categoryorder='array', categoryarray=score_order)
fig.update_yaxes(title='Tempo de entrega (dias)')

fig.show()

Nossa hipótese inicial é confirmada. Podemos claramente observar uma relação direta entre o tempo de entrega e a pontuação da avaliação. À medida que o tempo de entrega diminui, a pontuação da avaliação tende a aumentar.

Quais são as cidades dos vendedores com menor/maior tempo de entrega¶

In [36]:
# Vendedores com melhor tempo de entrega
sellersdeliverytime = data.groupby("seller_city")["TimeToDeliveryinDays"].agg(["min", "max", "mean", "std", "count" ]).dropna().sort_values("mean").reset_index()
# Filtro para vendedores com 30 ou mais pedidos em seu histórico
sellersdeliverytime = sellersdeliverytime[sellersdeliverytime["count"]>=30]
In [37]:
fastestdeliverysellers = sellersdeliverytime[:10]
slowestdeliverysellers = sellersdeliverytime.sort_values("mean", ascending=False)[:10]
In [38]:
# Vendedores com entrega mais rápida.
fastestdeliverysellers
Out[38]:
seller_city min max mean std count
14 varzea paulista 2.58 21.07 5.877297 3.284975 37
41 hortolandia 1.39 71.09 7.587543 6.676258 350
53 ribeirao preto / sao paulo 2.45 27.32 7.944500 4.055726 40
60 rio claro 1.70 34.88 8.144035 6.283043 114
61 ferraz de vasconcelos 1.17 20.84 8.186316 6.443319 38
63 vicente de carvalho 1.54 42.43 8.309910 6.604570 221
67 poa 1.09 38.20 8.533029 6.005796 175
70 carazinho 3.16 19.13 8.640656 3.858348 61
79 betim 1.24 53.85 8.818312 6.242661 314
85 maua 1.51 42.57 8.977107 5.455932 484
In [39]:
# Vendedores com entrega mais lenta.
slowestdeliverysellers
Out[39]:
seller_city min max mean std count
515 mombuca 6.03 88.24 22.921515 17.782976 33
509 itaquaquecetuba 1.61 194.85 21.838281 12.911572 1734
507 congonhal 4.31 60.82 21.590463 12.736508 108
504 balneario camboriu 3.25 79.36 20.641613 17.449918 31
503 tubarao 6.01 81.33 19.930286 20.308568 35
499 foz do iguacu 2.98 92.78 19.490833 17.240529 192
492 bombinhas 5.13 68.10 18.875761 9.758911 92
491 criciuma 3.73 71.16 18.396000 15.760557 35
488 claudio 5.73 48.63 18.171318 8.158321 129
482 caucaia 3.14 138.88 17.841471 23.052995 34
In [40]:
# Juntando a pontuação média de avaliação à tabela acima.
avg_review_score_seller = data.groupby("seller_city")["review_score"].mean().dropna().sort_values(ascending=False).reset_index()
In [41]:
sellerPerf = sellersdeliverytime.merge(avg_review_score_seller, on="seller_city")
In [42]:
# Gráfico de regressão linear entre a média de tempo de entrega e a média da pontuação de avaliação
fig = px.scatter(sellerPerf, x='mean', y='review_score', trendline='ols', 
                 title='Relação entre a média de tempo de entrega e a média da pontuação de avaliação')

fig.update_xaxes(title='Média de tempo de entrega (dias)')
fig.update_yaxes(title='Média da pontuação de avaliação')

fig.show()

Estados com maior/menor tempo de entrega¶

In [43]:
# Top 10 estados com o maior tempo médio de entrega
highestTTDstates = data.groupby("customer_state")["TimeToDeliveryinDays"].mean().dropna().sort_values(ascending=False).reset_index()
highestTTDstates = highestTTDstates[:10]

# Gráfico de barras com o tempo médio de entrega por estado
fig = px.bar(highestTTDstates, x='TimeToDeliveryinDays', y='customer_state',
             orientation='h', title='Top 10 estados com o maior tempo médio de entrega')

fig.update_yaxes(title='Estado')
fig.update_xaxes(title='Tempo médio de entrega (dias)')

fig.show()
In [44]:
# Top 10 estados com o menor tempo médio de entrega
lowestTTDstates = data.groupby("customer_state")["TimeToDeliveryinDays"].mean().dropna().sort_values(ascending=True).reset_index()
lowestTTDstates = lowestTTDstates[:10]

# Gráfico de barras com o tempo médio de entrega por estado
fig = px.bar(lowestTTDstates, x='TimeToDeliveryinDays', y='customer_state',
             orientation='h', title='Top 10 estados com o menor tempo médio de entrega')

fig.update_yaxes(title='Estado')
fig.update_xaxes(title='Tempo médio de entrega (dias)')

fig.show()

Infraestrutura Logística: Explorar melhorias na infraestrutura logística nessas regiões pode ser crucial para reduzir os prazos de entrega. Investir em rotas eficientes, centros de distribuição estratégicos e parcerias logísticas pode otimizar o processo.

Demanda e Planejamento: Entender a demanda específica desses estados permite um planejamento mais eficaz. Ajustar os estoques com base nas necessidades regionais pode contribuir para entregas mais rápidas e eficientes.

Parcerias Locais: Estabelecer parcerias sólidas com empresas locais de transporte e distribuição pode ser uma estratégia eficaz. Essas parcerias podem proporcionar uma compreensão mais profunda das condições locais e contribuir para a agilidade nas entregas.

Estratégias de Roteamento Inteligente: Implementar sistemas de roteamento inteligente, utilizando tecnologias como rastreamento em tempo real e análise de dados, pode otimizar a entrega, considerando fatores específicos de cada região.

Comunicação Transparente: Fornecer informações transparentes aos clientes sobre os prazos de entrega estimados pode gerar confiança. A comunicação proativa sobre eventuais atrasos e atualizações em tempo real pode melhorar a experiência do cliente.

Análise Contínua: Realizar análises periódicas dos processos logísticos, identificando gargalos e oportunidades de melhoria, é fundamental. A adaptação constante às condições específicas de cada região contribuirá para uma entrega mais eficiente.

Ao focar nessas áreas, a empresa pode desenvolver estratégias direcionadas para melhorar a eficiência logística, proporcionando uma experiência mais positiva aos clientes desses estados da região Nordeste.

Como o tempo médio de entrega varia ao longo do tempo?¶

In [45]:
# Mediana do tempo de entrega por ano
deliverytimevstime = data.groupby(data["order_purchase_timestamp"].dt.year)["TimeToDeliveryinDays"].median().dropna()

# Convertendo ano em categoria (e não um número)
deliverytimevstime.index = deliverytimevstime.index.astype(str)

# Gráfico de barras com a mediana do tempo de entrega por ano
fig = px.bar(deliverytimevstime, x=deliverytimevstime.index, y='TimeToDeliveryinDays',
             title='Mediana do tempo de entrega por ano')

fig.update_xaxes(title='Ano')
fig.update_yaxes(title='Mediana de entrega em dias')

fig.show()

Podemos ver que a empresa fez uma melhoria significativa no tempo de entrega. Em 2016, o tempo médio de entrega excedia 17 dias, enquanto em 2017 ele caiu para cerca de 12 dias e para cerca de 9 dias em 2018.

Como a pontuação média das avaliações varia ao longo do tempo¶

In [46]:
# Média da pontuação da avaliação por ano
scorevstime = data.groupby(data["order_purchase_timestamp"].dt.year)["review_score"].mean().dropna()

# Converte o índice do grupo em uma categoria
scorevstime.index = scorevstime.index.astype(str)

# Gráfico de barras com a média da pontuação da avaliação por ano
fig = px.bar(scorevstime, x=scorevstime.index, y='review_score',
             title='Média da pontuação da avaliação por ano')

fig.update_xaxes(title='Ano')
fig.update_yaxes(title='Pontuação média da avaliação')

fig.show()

Os clientes estão dando pontuações melhores em 2017 e 2018 do que em 2016.

Categorias de produtos mais vendidas¶

In [47]:
# Agrupamento
top_categ_by_revenue = data.groupby("product_category_name").agg({'order_id':'nunique','payment_value':'sum'}).sort_values("payment_value", ascending=False)[:10]
top_categ_by_revenue.rename(columns={"order_id":"NumOfOrders", "payment_value":"Revenues"}, inplace=True)
In [48]:
top_categ_by_revenue
Out[48]:
NumOfOrders Revenues
product_category_name
cama_mesa_banho 9313 1725465.67
beleza_saude 8770 1646292.53
informatica_acessorios 6649 1592611.66
moveis_decoracao 6398 1427214.01
relogios_presentes 5576 1420682.17
esporte_lazer 7669 1390581.87
utilidades_domesticas 5843 1091709.15
ferramentas_jardim 3496 834757.45
automotivo 3877 802254.74
cool_stuff 3599 772616.70

Mapa coroplético com o tempo de entrega em dias por cada unidade federativa¶

Para fazer isso, precisamos de um arquivo que tenha as delimitações dos estados brasileiros em forma de polígonos. Podemos encontrar esse arquivo aqui

Para ler o arquivo, utilizaremos o Geopandas! É uma biblioteca Python que estende o poder do Pandas para permitir a manipulação de dados geoespaciais.

In [49]:
# Agrupando os dados por estado e obter a média do tempo de entrega
mean_delivery_time_by_state = data.groupby('customer_state')['TimeToDeliveryinDays'].mean().reset_index()

mean_delivery_time_by_state
Out[49]:
customer_state TimeToDeliveryinDays
0 AC 20.544348
1 AL 24.498128
2 AM 26.382395
3 AP 28.070000
4 BA 19.152422
5 CE 20.646928
6 DF 12.949766
7 ES 15.532738
8 GO 15.249974
9 MA 21.534691
10 MG 11.948940
11 MS 15.493250
12 MT 17.858972
13 PA 23.476657
14 PB 20.425355
15 PE 18.200039
16 PI 19.326229
17 PR 11.963043
18 RJ 15.182924
19 RN 19.686383
20 RO 19.822920
21 RR 28.251591
22 RS 15.160591
23 SC 14.862988
24 SE 21.265183
25 SP 8.720986
26 TO 17.144225
In [50]:
import geopandas as gpd

geojson_file = './data/raw/uf.json'
geojson_data = gpd.read_file(geojson_file)
In [51]:
geojson_data.head(10)
Out[51]:
UF_05 MICRO MESO REGIAO NOME_UF GEOCODIGO geometry
0 AC BRASILEIA VALE DO ACRE Norte Acre 12 POLYGON ((-73.80098 -7.11146, -73.73763 -7.134...
1 AL ALAGOANA DO SERTAO DO SAO FRANCISCO AGRESTE ALAGOANO Nordeste Alagoas 27 POLYGON ((-38.23724 -9.32937, -38.20273 -9.296...
2 AM ALTO SOLIMOES CENTRO AMAZONENSE Norte Amazonas 13 POLYGON ((-73.80098 -7.11146, -73.79617 -7.102...
3 AP AMAPA NORTE DO AMAPA Norte Amapá 16 POLYGON ((-54.87178 2.43408, -54.79692 2.43935...
4 BA ALAGOINHAS CENTRO NORTE BAIANO Nordeste Bahia 29 POLYGON ((-46.60921 -11.25550, -46.58164 -11.2...
5 CE BAIXO CURU CENTRO-SUL CEARENSE Nordeste Ceará 23 POLYGON ((-41.41398 -3.34957, -41.41194 -3.340...
6 DF BRASILIA DISTRITO FEDERAL Centro-Oeste DF 53 POLYGON ((-48.28216 -15.82915, -48.26746 -15.8...
7 ES AFONSO CLAUDIO CENTRAL ESPIRITO-SANTENSE Sudeste Espírito Santo 32 MULTIPOLYGON (((-41.87939 -20.75908, -41.87372...
8 GO ANAPOLIS CENTRO GOIANO Centro-Oeste Goiás 52 POLYGON ((-53.25072 -17.61863, -53.24694 -17.6...
9 MA AGLOMERACAO URBANA DE SAO LUIS CENTRO MARANHENSE Nordeste Maranhão 21 MULTIPOLYGON (((-48.75472 -5.34878, -48.58427 ...
In [52]:
# Criando tempo de entrega dentro do geojson
geojson_data['tempo_entrega'] = mean_delivery_time_by_state['TimeToDeliveryinDays']

geojson_data.head()
Out[52]:
UF_05 MICRO MESO REGIAO NOME_UF GEOCODIGO geometry tempo_entrega
0 AC BRASILEIA VALE DO ACRE Norte Acre 12 POLYGON ((-73.80098 -7.11146, -73.73763 -7.134... 20.544348
1 AL ALAGOANA DO SERTAO DO SAO FRANCISCO AGRESTE ALAGOANO Nordeste Alagoas 27 POLYGON ((-38.23724 -9.32937, -38.20273 -9.296... 24.498128
2 AM ALTO SOLIMOES CENTRO AMAZONENSE Norte Amazonas 13 POLYGON ((-73.80098 -7.11146, -73.79617 -7.102... 26.382395
3 AP AMAPA NORTE DO AMAPA Norte Amapá 16 POLYGON ((-54.87178 2.43408, -54.79692 2.43935... 28.070000
4 BA ALAGOINHAS CENTRO NORTE BAIANO Nordeste Bahia 29 POLYGON ((-46.60921 -11.25550, -46.58164 -11.2... 19.152422
In [53]:
# Mapa
fig = px.choropleth_mapbox(geojson_data, 
                           geojson=geojson_data.geometry, 
                           locations=geojson_data.index, 
                           color='tempo_entrega',
                           color_continuous_scale='YlOrRd',
                           mapbox_style='open-street-map',
                           zoom=3, center={'lat': -15.788497, 'lon': -47.879873},
                           opacity=0.5)

fig.show()

Considerações sobre a Análise¶

A análise abrangente dos dados da Olist proporcionou insights valiosos. Destacam-se a importância de cultivar relacionamentos com os principais clientes, a concentração de atividade econômica em algumas cidades estratégicas e a necessidade de gerenciar eficazmente as expectativas do cliente, especialmente em categorias de produtos complexas. Além do cuidado em diversificar as fontes de receita, evitando uma concentração.

A análise logística revelou uma relação direta entre o tempo de entrega e a satisfação do cliente, indicando a necessidade de melhorias na infraestrutura logística e adaptação às demandas regionais. A visualização geoespacial ofereceu uma perspectiva valiosa para ajustar estratégias com base nas condições específicas de cada região.

A evolução positiva ao longo do tempo no tempo de entrega e nas pontuações médias de avaliação reflete a eficácia das iniciativas implementadas. Em resumo, a análise fornece orientações cruciais para estratégias futuras, visando otimização, aprimoramento da experiência do cliente e crescimento sustentável no mercado de comércio eletrônico.

Referências¶

  • Brazilian Ecommerce EDA + RFM + NMF Notebook 1
  • GIS Dataset
In [ ]: